// 面向对象轮播图 构造函数
class CreateBannerObj{

    // 构造器 存储 参数 和 全局变量
    constructor(element , bannerArr){
        // 定义属性 存储 形参数据
        this.ele = element ;
        this.arr = bannerArr ; 

        // 定义属性 存储 全局变量
        this.oUl;
        this.oOl;
        this.oDiv;
        this.oUlLis;
        this.oOlLis;
        this.liWidth;
        this.time;

        this.index = 1 ;
        this.res = true ;

    } 

    // 函数方法

    // 入口函数
    // 在一个函数中调用所有需要执行的函数程序
    // 也就是 只要 调用一个函数 就调用了所有需要调用的函数
    // 入口函数 必须定义名称是 init 1
    init(){
        this.setPage();
        this.autoLoop();
        this.setMouseEnterLeave();
        this.setClick();
        this.hide();
    }


    // 动态生成HTML页面
    setPage(){
        this._this = this ;

        // 创建 三个标签节点 ul ol div
        this.oUl = document.createElement('ul');
        this.oOl = document.createElement('ol');
        this.oDiv = document.createElement('div');

        // 设定 三个标签节点的内容 

        // 根据数组内容 动态生成 ul>li 和 ol>li
        let ulLiStr = '' ;
        let olLiStr = '' ;

        // 循环遍历 数组 
        this.arr.forEach((item,key)=>{
            // item 是 数组单元的数据数值 也就是 存储图片数据的对象

            // 动态生成 ul>li
            ulLiStr += `<li><img src="../img/images/${item.url}" alt=""></li>` ;

            // 动态生生 ul>li
            // 给 第一个li标签 添加class,active 也就是 背景颜色是红色
            // 给 每个li标签 添加 num属性属性值是索引下标
            olLiStr += key === 0 ? `<li num="${key}" name="olLi" class="active"></li>` : `<li num="${key}" name="olLi"></li>` ;
        })

        // 将 生成的字符串 写入 标签节点中
        this.oUl.innerHTML = ulLiStr ;
        this.oOl.innerHTML = olLiStr ;

        // div标签节点中 内容是 固定的左右按钮
        this.oDiv.innerHTML = '<a name="left" href="JavaScript:;">&lt;</a><a name="right" href="JavaScript:;">&gt;</a>' ;


        // 将 三个标签节点写入 banner,div标签中
        this.ele.appendChild( this.oUl );
        this.ele.appendChild( this.oOl );
        this.ele.appendChild( this.oDiv );

        // 获取生成的 原始 ul>li 标签对象
        this.oUlLis = this.oUl.querySelectorAll('li');
            
        // 获取生成的 原始 ol>li 标签对象
        this.oOlLis = this.oOl.querySelectorAll('li');

        // 获取li标签的宽度占位 内容+padding+border
        this.liWidth = this.oUlLis[0].offsetWidth ; 

        // 克隆 原始ul中 第一个li标签对象
        const oFirstClone = this.oUlLis[0].cloneNode( true ) ;

        // 克隆 原始ul中 最后一个li标签对象
        const oLastClone = this.oUlLis[ this.oUlLis.length-1 ].cloneNode( true ) ;

        // 将 克隆的第一个 写入 ul的最后 
        this.oUl.appendChild( oFirstClone );

        // 将 克隆的最后一个 写入 ul的起始 
        this.oUl.insertBefore( oLastClone , this.oUlLis[0] );

        // 重新设定ul宽度 
        // 克隆之后li标签个数 * li标签宽度
        // 克隆之后li标签个数是数组单元个数+2
        this.oUl.style.width = ( this.arr.length+2 ) * this.liWidth + 'px' ;

        // 将ul向左定位一个li的宽度
        this.oUl.style.left = -this.liWidth + 'px' ;
    }

    // 自动轮播
    autoLoop(){
        // 设定定时器
        this.time = setInterval( ()=>{
            // 变量累加1
            this.index++ ;

            // index变量累加后 设定 对应的焦点样式
            this.setFocusStyle();

            // 通过 move() 运动函数 完成 ul标签 定位的切换

            // 回调函数使用 bind() 方法 修改this指向 
            // 当前 this 指向实例化对象 修改回调函数的this指向是当前this也就是实例化对象
            move( this.oUl , { left : -this.index*this.liWidth } , this.loopEnd.bind( this ) );

            // 使用 箭头函数 调用执行 回调函数 也可以
            // move( this.oUl , { left : -this.index*this.liWidth } , ()=>{
            //     this.loopEnd() ;
            // } );


        } , 3000 )
    }

    // 函数3    焦点样式切换
    setFocusStyle(){
        // 清除所有的ol>li焦点样式
        this.oOlLis.forEach( (item)=>{
            // item 是 oL>li 标签对象 
            item.classList.remove('active');
        })

        // 如果 显示 当前ul中最后一个li 给 ol>li 中的 第一个添加样式
        if( this.index === this.arr.length+2-1 ){
            // 给 索引是0 的 第一个 ol>li 添加 css样式
            this.oOlLis[ 0 ].classList.add('active');
        
        // 如果 显示 点券ul中第一个li 给 ol>li 中的 最后一个添加样式
        }else if( this.index === 0 ){
            // 给 ol>li 的最后一个 添加 css样式
            this.oOlLis[ this.oOlLis.length-1 ].classList.add('active');

        // 其他情况 给 当期ul>li索引下标 -1 对应的 ol>li 添加样式
        }else{
            // 当前 显示的ul>li的索引下标 也就是 index
            // 数值 -1 是 对应的 ol>li 的 索引下标 
            this.oOlLis[ this.index-1 ].classList.add('active');                
        }

    }

    // 函数4    move运动函数执行结束触发的回调函数
    loopEnd(){

        //  如果是 当前ul的最后一个li 运动结束 立即切换到 当前ul的第二个li
        //  也就是 index 是 最后一个li的索引下标 也就是 arr.length+2-1 
        //  切换到 当前ul的第二个li 也就是 index 赋值 1 
        if( this.index === this.arr.length+2-1 ){
            // 给 变量 赋值 当前ul第二个li的索引下标
            this.index = 1 ;
        

        //  如果是 当前ul的第一个li 运动结束 立即切换到 当前ul的倒数二个li
        //  也就是 index 是 第一个li的索引下标 也就是 0
        //  切换到 当前ul的倒数第二个li 也就是 index 赋值 arr.length+2-1-1

        }else if( this.index === 0 ){
            // 给 变量 赋值 当前ul的倒数第二个li的索引下标
            this.index = this.arr.length+2-1-1;
        }

        //  给 ul 执行定位 瞬间切换
        // 根据新的index数值 给 ul标签 做瞬间定位切换
        this.oUl.style.left = -this.index * this.liWidth + 'px';

        // 运动结束 也就是 一次ul切换完成
        // 给变量赋值原始值 可以 执行下一次运动
        this.res = true ;
    }


    // 函数5    鼠标的移入移出
    setMouseEnterLeave(){
        // 鼠标移入 
        this.ele.addEventListener( 'mouseenter' , ()=>{
            // 清除定时器
            clearInterval( this.time );
        })

        // 鼠标移出
        this.ele.addEventListener( 'mouseleave' , ()=>{
            // 再次调用自动轮播函数
            this.autoLoop();
        })
    }


    // 函数6    点击事件
    setClick(){
        // 给 轮播图div添加点击事件
        this.ele.addEventListener('click' , (e)=>{
            // 判断 如果 事件对象 e.target 的name属性值是 left 点击的是左切换按钮
            if( e.target.getAttribute('name') === 'left' ){
                // 在执行程序之前 先 判断 变量储存的数据
                if( this.res ){
                    // 如果是原始值true 赋值存储其他数值false
                    // 正常执行之后的程序
                    this.res = false ;
                }else{
                    // 如果不是原始值true
                    // 触发执行 return 
                    // 终止执行之后的程序
                    return ;
                }

                // 切换显示上一个li 
                // 也就是 索引下标 累减 1 
                this.index-- ;

                // 调用函数 重新设定 焦点按钮css样式
                this.setFocusStyle();

                // 根据新的索引下标 通过move()运动函数 
                // 动画效果 完成ul标签的重新定位
                move( this.oUl , { left : -this.index*this.liWidth } , this.loopEnd.bind(this) );
            
            // 判断 如果 事件对象 e.target 的name属性值是 right 点击的是右切换按钮
            }else if( e.target.getAttribute('name') === 'right' ){
  
                // 在执行程序之前 先 判断 变量储存的数据
                if( this.res ){
                    // 如果是原始值true 赋值存储其他数值false
                    // 正常执行之后的程序
                    this.res = false ;
                    
                }else{
                    // 如果不是原始值true
                    // 触发执行 return 
                    // 终止执行之后的程序
                    return ;
                }

                // 切换显示下一个li 
                // 也就是 索引下标 累加 1 
                this.index++ ;

                // 调用函数 重新设定 焦点按钮css样式
                this.setFocusStyle();

                // 根据新的索引下标 通过move()运动函数 
                // 动画效果 完成ul标签的重新定位
                move( this.oUl , { left : -this.index*this.liWidth } , this.loopEnd.bind(this) );
            
            // 判断 如果 事件对象 e.target 的name属性值是 olLi 点击的是焦点按钮
            }else if( e.target.getAttribute('name') === 'olLi' ){
                // 在执行程序之前 先 判断 变量储存的数据
                if( this.res ){
                    // 如果是原始值true 赋值存储其他数值false
                    // 正常执行之后的程序
                    this.res = false ;
                }else{
                    // 如果不是原始值true
                    // 触发执行 return 
                    // 终止执行之后的程序
                    return ;
                }


                // 获取当前 点击的ol>li标签 num属性的属性值 
                // 也就是 点击的ol>li标签的 索引下标 
                // num属性值+1 是 对应的 ul>li 的索引下标
                // num属性值 是 字符串类型 必须要转化为数值类型 再 执行+1 运算
                this.index = Number( e.target.getAttribute('num') ) + 1 ;

                // 调用函数 重新设定 焦点按钮css样式
                this.setFocusStyle();

                // 根据新的索引下标 通过move()运动函数 
                // 动画效果 完成ul标签的重新定位
                move( this.oUl , { left : -this.index*this.liWidth } , this.loopEnd.bind(this) );
            }
        })
    }


    // 函数7    浏览器最小化隐藏事件
    hide(){
        // 给 document 添加 浏览器显示状态监听
        document.addEventListener( 'visibilitychange' , ()=>{
            // 如果 浏览器显示状态描述 是 hidden 
            if( document.visibilityState === 'hidden' ){
                // 证明当前浏览器隐藏最小化
                // 清除定时器
                clearInterval( this.time );
            
            // 如果 浏览器显示状态描述 是 visible 
            }else if( document.visibilityState === 'visible' ){
                // 证明当前浏览器 显示
                // 再次调用 自动轮播函数
                this.autoLoop();
            }
        })
    
    }

}